﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.IO;

using Vitruvian.Serialization;
using Vitruvian.Serialization.Formatters;
using Vitruvian.Windows.Serialization.Formatters;
using Vitruvian.Logging;
using Vitruvian.Testing;

namespace SpotItComponents
{
    [ContainsTests("Components.SymbolStuff")]
    [OptimisticSerialization]
    public class Symbol : Component
    {
        #region Private Data Members
        private static Logger logger = Logger.GetLogger(typeof(Symbol));
        #endregion

        #region Public Properties

        public string Label { get; set; }

        [ImageFormatter]
        public Image SymbolImage { get; set; }

        public bool Selected { get; set; }

        [DontSerialize]
        public SymbolSet ContainingSymbolSet = null;

        public Bitmap RotateImage(float angle, int desiredSize)
        {
            int W, H;
            double dW = (double) SymbolImage.Width;
            double dH = (double) SymbolImage.Height;
            float rotateAtX = Convert.ToSingle(dW / 2);
            float rotateAtY = Convert.ToSingle(dH / 2);
            double radians;
            double dSin;
            double dCos;

            double degrees = Convert.ToDouble(angle);
            if (degrees <= 90 || (degrees > 180 && degrees <= 270))
            {
                radians = 0.0174532925 * degrees;
                dSin = Math.Sin(radians);
                dCos = Math.Cos(radians);
                W = Math.Abs((int)(dH * dSin + dW * dCos));
                H = Math.Abs((int)(dW * dSin + dH * dCos));
            }
            else
            {
                radians = 0.0174532925 * (degrees - 90);
                dSin = Math.Sin(radians);
                dCos = Math.Cos(radians);
                W = Math.Abs((int)(dW * dSin + dH * dCos));
                H = Math.Abs((int)(dH * dSin + dW * dCos));
            }
            logger.DebugFormat("degrees={0}, W={1}, H={2}", degrees, W, H);

            float xScale = Convert.ToSingle(desiredSize) / SymbolImage.Width;
            float yScale = Convert.ToSingle(desiredSize) / SymbolImage.Height;

            W = desiredSize;
            H = desiredSize;

            logger.DebugFormat("RotateAtX={0}, RotateAtY={1}", rotateAtX, rotateAtY);

            //create a new empty bitmap to hold rotated image
            Bitmap bmpRet = new Bitmap(W, H);
            bmpRet.SetResolution(SymbolImage.HorizontalResolution, SymbolImage.VerticalResolution);

            //make a graphics object from the empty bitmap
            Graphics g = Graphics.FromImage(bmpRet);

            // Scale the drawing
            g.ScaleTransform(xScale, yScale);

            //Put the rotation point in the "center" of the image
            g.TranslateTransform(rotateAtX, rotateAtY);

            //rotate the image
            g.RotateTransform(angle);

            //move the image back
            g.TranslateTransform(-rotateAtX, -rotateAtY);

            //draw passed in image onto graphics object
            g.DrawImage(SymbolImage, new PointF(0, 0));

            return bmpRet;
        }
        #endregion
        
        #region Constructors and Creation Methods
        /// <summary>
        /// Default constructor
        /// </summary>
        public Symbol()
        {
            Initialize(null, null, null);
        }

        /// <summary>
        /// Use this Create Methdo to create an empty symbol in a set
        /// </summary>
        /// <param name="containingSybmolSet">The set that wil contain the new symbol</param>
        /// <returns></returns>
        public static Symbol SymbolInASet(SymbolSet containingSybmolSet)
        {
            Symbol s = new Symbol();
            s.Initialize(null, null, containingSybmolSet);
            return s;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="label">A label from the new symbol</param>
        /// <param name="symbolImage">The symbols image</param>
        /// <returns></returns>
        public static Symbol CreateSymbolWithLabelImage(string label, Image symbolImage)
        {
            Symbol s = new Symbol();
            s.Initialize(label, symbolImage, null);
            return s;
        }

        public static Symbol CreateSymbolWithLabelImageInSet(string label, Image symbolImage, SymbolSet containingSymbolSet)
        {
            Symbol s = new Symbol();
            s.Initialize(label, symbolImage, containingSymbolSet);
            return s;
        }

        public static Symbol CopySymbol(Symbol origSymbol)
        {
            Symbol s = new Symbol();
            s.Initialize(origSymbol.Label, origSymbol.SymbolImage, null);
            return s;
        }

        public static Symbol CopySymbolIntoSet(Symbol origSymbol, SymbolSet containingSymbolSet)
        {
            Symbol s = new Symbol();
            s.Initialize(origSymbol.Label, origSymbol.SymbolImage, containingSymbolSet);
            return s;
        }

        #endregion

        #region Private Methods
        private void Initialize(string label, Image myImage, SymbolSet containingSymbolSet)
        {
            logger.DebugFormat("Initialize a new symbol {0}", label);
            Label = (!string.IsNullOrEmpty(label)) ? label : "Unnamed Symbol";
            SymbolImage = myImage;
            ContainingSymbolSet = containingSymbolSet;
        }
        #endregion

        #region Unit Test Cases
#if (DEBUG)
        [Test(1)]
        private void Test_Symbol_Constructors_Initialize_and_Properties()
        {
            SymbolSet ss1 = new SymbolSet("Test Set A", false);
            SymbolSet ss2 = new SymbolSet("Test Set B", false);

            Symbol s1 = new Symbol();
            Assert.IsNotEqual(Guid.Empty, s1.Id);
            Assert.IsEqual("Unnamed Symbol", s1.Label);
            Assert.IsNull(s1.SymbolImage);
            Assert.IsNull(s1.ContainingSymbolSet);

            Image testImage = new Bitmap("../../../Symbols/Alligator.jpg");
            s1 = Symbol.CreateSymbolWithLabelImageInSet("Alligator", testImage, ss1);
            Assert.IsNotEqual(Guid.Empty, s1.Id);
            Assert.IsEqual("Alligator", s1.Label);
            Assert.IsNotNull(s1.SymbolImage);
            Assert.IsEqual(testImage, s1.SymbolImage);
            Assert.IsEqual(ss1, s1.ContainingSymbolSet);

            Symbol s2 = Symbol.CopySymbolIntoSet(s1,ss2);
            Assert.IsNotEqual(Guid.Empty, s2.Id);
            Assert.IsNotEqual(s1.Id, s2.Id);
            Assert.IsEqual("Alligator", s2.Label);
            Assert.IsNotNull(s2.SymbolImage);
            Assert.IsEqual(testImage, s2.SymbolImage);
            Assert.IsEqual(ss2, s2.ContainingSymbolSet);

            Symbol s3 = Symbol.CreateSymbolWithLabelImage("Crab", new Bitmap("../../../Symbols/Crab.jpg"));
            Assert.IsNotEqual(Guid.Empty, s3.Id);
            Assert.IsEqual("Crab", s3.Label);
            Assert.IsNotNull(s3.SymbolImage);
            Assert.IsNull(s3.ContainingSymbolSet);

            Symbol s4 = Symbol.SymbolInASet(ss1);
            Assert.IsNotEqual(Guid.Empty, s4.Id);
            Assert.IsEqual("Unnamed Symbol", s4.Label);
            Assert.IsNull(s4.SymbolImage);
            Assert.IsNotNull(s4.ContainingSymbolSet);
            Assert.IsEqual(ss1, s4.ContainingSymbolSet);

            Symbol s5 = Symbol.CreateSymbolWithLabelImageInSet("Crab", new Bitmap("../../../Symbols/Crab.jpg"), ss1);
            Assert.IsNotEqual(Guid.Empty, s5.Id);
            Assert.IsEqual("Crab", s5.Label);
            Assert.IsNotNull(s5.SymbolImage);
            Assert.IsNotNull(s5.ContainingSymbolSet);
            Assert.IsEqual(ss1, s5.ContainingSymbolSet);

            Symbol s6 = Symbol.CopySymbol(s5);
            Assert.IsNotEqual(Guid.Empty, s6.Id);
            Assert.IsEqual("Crab", s6.Label);
            Assert.IsNotNull(s6.SymbolImage);
            Assert.IsNull(s6.ContainingSymbolSet);

        }

        [Test(2)]
        private void Test_Compare()
        {
            SymbolSet ss1 = new SymbolSet();
            SymbolSet ss2 = new SymbolSet();
            Image testImage = new Bitmap("../../../Symbols/Alligator.jpg");

            // Create a symbol from the image
            Symbol s1 = CreateSymbolWithLabelImageInSet("Test Symbol 1", testImage, ss1);
            Assert.IsNotEqual(Guid.Empty, s1.Id);
            Assert.IsEqual("Test Symbol 1", s1.Label);
            Assert.IsNotNull(s1.SymbolImage);
            Assert.IsEqual(testImage, s1.SymbolImage);
            Assert.IsEqual(ss1, s1.ContainingSymbolSet);

            Assert.IsEqual(0, s1.CompareTo(s1));

            Symbol s2 = Symbol.CopySymbolIntoSet(s1, ss2);
            Assert.IsNotEqual(0, s1.CompareTo(s2));

            s2.Id = s1.Id;
            Assert.IsEqual(0, s1.CompareTo(s2));

            Assert.IsTrue(s1.Equals(s2));
        }

        [Test(2)]
        private void Test_RotateImage()
        {
            SymbolSet ss1 = new SymbolSet();
            Image testImage = new Bitmap("../../../Symbols/Alligator.jpg");

            // Create a symbol from the image
            Symbol s1 = CreateSymbolWithLabelImageInSet("Test Symbol 1", testImage, ss1);
            Assert.IsNotEqual(Guid.Empty, s1.Id);
            Assert.IsEqual("Test Symbol 1", s1.Label);
            Assert.IsNotNull(s1.SymbolImage);
            Assert.IsEqual(testImage, s1.SymbolImage);
            Assert.IsEqual(ss1, s1.ContainingSymbolSet);


            Image rotatedImage = s1.RotateImage(15, 150);
            rotatedImage.Save("../../../Symbols/Alligator-15DegreeRotation-150pixels.jpg");

            rotatedImage = s1.RotateImage(45, 150);
            rotatedImage.Save("../../../Symbols/Alligator-45DegreeRotation-150pixels.jpg");

            rotatedImage = s1.RotateImage(75, 150);
            rotatedImage.Save("../../../Symbols/Alligator-75DegreeRotation-150pixels.jpg");

            rotatedImage = s1.RotateImage(90, 150);
            rotatedImage.Save("../../../Symbols/Alligator-90DegreeRotation-150pixels.jpg");

            rotatedImage = s1.RotateImage(105, 150);
            rotatedImage.Save("../../../Symbols/Alligator-105DegreeRotation-152pixels.jpg");

            rotatedImage = s1.RotateImage(135, 150);
            rotatedImage.Save("../../../Symbols/Alligator-135DegreeRotation-150pixels.jpg");

            rotatedImage = s1.RotateImage(145, 150);
            rotatedImage.Save("../../../Symbols/Alligator-145DegreeRotation-150pixels.jpg");

            rotatedImage = s1.RotateImage(180, 150);
            rotatedImage.Save("../../../Symbols/Alligator-180DegreeRotation-150pixels.jpg");

            rotatedImage = s1.RotateImage(205, 150);
            rotatedImage.Save("../../../Symbols/Alligator-205DegreeRotation-150pixels.jpg");

            rotatedImage = s1.RotateImage(225, 150);
            rotatedImage.Save("../../../Symbols/Alligator-225DegreeRotation-150pixels.jpg");

            rotatedImage = s1.RotateImage(245, 150);
            rotatedImage.Save("../../../Symbols/Alligator-245DegreeRotation-150pixels.jpg");

            rotatedImage = s1.RotateImage(270, 150);
            rotatedImage.Save("../../../Symbols/Alligator-270DegreeRotation-150pixels.jpg");

            rotatedImage = s1.RotateImage(315, 150);
            rotatedImage.Save("../../../Symbols/Alligator-315DegreeRotation-150pixels.jpg");
        }

#endif
        #endregion

    }
}
